package org.piggy.core.filter;

import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.piggy.common.constants.FilterConst;
import org.piggy.common.rule.Rule;
import org.piggy.core.context.GatewayContext;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

@Slf4j
public class GatewayFilterChainFactory implements FilterFactory {

    private static class SingletonInstance {
        private static final GatewayFilterChainFactory INSTANCE = new GatewayFilterChainFactory();
    }

    public static GatewayFilterChainFactory getInstance(){
        return SingletonInstance.INSTANCE;
    }

    private final Cache<String, GatewayFilterChain> chainCache = Caffeine.newBuilder().recordStats().expireAfterWrite(10, TimeUnit.MINUTES).build();

    private final Map<String, Filter> processorFilterIdMap = new ConcurrentHashMap<>();

    public GatewayFilterChainFactory() {
        ServiceLoader<Filter> serviceLoader = ServiceLoader.load(Filter.class);
        serviceLoader.stream().forEach(filterProvider -> {
            Filter filter = filterProvider.get();
            FilterAspect annotation = filter.getClass().getAnnotation(FilterAspect.class);
            log.info("load filter success:{},{},{},{}", filter.getClass(), annotation.id(), annotation.name(), annotation.order());
            String filterId = annotation.id();
            processorFilterIdMap.put(filterId, filter);
        });
    }

    @Override
    public GatewayFilterChain buildFilterChain(GatewayContext ctx) {
        return doBuildFilterChain(ctx.getRule());
    }

    public GatewayFilterChain doBuildFilterChain(Rule rule) {
        GatewayFilterChain chain = new GatewayFilterChain();
        List<Filter> filters = new ArrayList<>();
        if(rule != null){
            Set<Rule.FilterConfig> filterConfigs = rule.getFilterConfigs();
            if(filterConfigs != null){
                for (Rule.FilterConfig filterConfig : filterConfigs) {
                    String filterId = filterConfig.getId();
                    if (StringUtils.isNotEmpty(filterId)) {
                        Filter filter = getFilterInfo(filterId);
                        if(filter != null){
                            filters.add(filter);
                        }
                    }
                }
            }
        }
        filters.add(getFilterInfo(FilterConst.GRAY_FILTER_ID));
        filters.add(getFilterInfo(FilterConst.LOAD_BALANCE_FILTER_ID));
        filters.add(getFilterInfo(FilterConst.ROUTER_FILTER_ID));
        filters.sort(Comparator.comparingInt(Filter::getOrder));
        chain = chain.addFilterList(filters);
        return chain;
    }

    @Override
    public Filter getFilterInfo(String filterId){
        return processorFilterIdMap.get(filterId);
    }
}
